package com.gobang.api;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.gobang.game.*;
import com.gobang.model.User;
import com.gobang.model.UserMapper;
import jakarta.annotation.Resource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;

import java.io.IOException;

@Component
public class gameAPI extends TextWebSocketHandler {
    private ObjectMapper objectMapper = new ObjectMapper();
    @Autowired
    private RoomManager roomManager;
    // 这个是管理 game ⻚⾯的会话
    @Autowired
    private OnlineUserManager onlineUserManager;
    @Resource //通过数据库用这个
    private UserMapper userMapper;
    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        GameReadyResponse gameReadyResponse = new GameReadyResponse();

        //1.⾸先需要检测⽤⼾的登录状态. 从 Session 中拿到当前⽤⼾信息.
        User user = (User) session.getAttributes().get("user");

        if (user == null) {
            gameReadyResponse.setOk(false);
            gameReadyResponse.setReason("用户未登录");
            session.sendMessage(new
                    TextMessage(objectMapper.writeValueAsString(gameReadyResponse)));
            return;
        }
        //2.然后要判定当前玩家是否是在房间中.
        Room room = roomManager.getRoomByUserId(user.getUserId());
        if (room == null) {
            gameReadyResponse.setOk(false);
            gameReadyResponse.setReason("用户未匹配成功，进入房间失败");
            session.sendMessage(
                    new TextMessage(objectMapper.writeValueAsString(gameReadyResponse)));
            return;
        }
        System.out.println("连接游戏! roomId=" + room.getRoomId() + ", userId=" +
                user.getUserId());
        //3.接下来进⾏多开判定.如果玩家已经在游戏中, 则不能再次连接

        if (onlineUserManager.getFromGameHall(user.getUserId()) != null ||
                onlineUserManager.getFromGameRoom(user.getUserId()) != null) {
            //如果一个账号，一边在游戏大厅，一边在游戏房间，也视为多开~
            gameReadyResponse.setOk(true);
            gameReadyResponse.setReason("禁止多开");
            System.out.println("多开");
            gameReadyResponse.setMessage("repeatConnection");
            session.sendMessage(new
                    TextMessage(objectMapper.writeValueAsString(gameReadyResponse)));
            //直接关闭太激进，还是返回一个特殊的message，供用客户端分析
            //session.close();
            return;
        }
        //4.设置玩家上线
        onlineUserManager.enterGameRoom(user.getUserId(), session);
        //4.把两个玩家放到对应的房间对象中. 当两个玩家都建⽴了连接, 房间就放满了.这个时候通知两个玩家
        //双⽅都准备就绪
        synchronized (room) {
            if (room.getUser1() == null) {
                room.setUser1(user);
                room.setWhiteUserId(user.getUserId());//设为先手
                System.out.println("userId=" + user.getUserId() + " 玩家1准备就绪!玩家是先手");
                return;
            }
            if (room.getUser2() == null) {
                room.setUser2(user);
                System.out.println("userId=" + user.getUserId() + " 玩家2准备就绪!");
                //当两个玩家都已经加入成功之后，就让服务器给两个玩家都玩会websocket的反应
                //通知两个玩家，游戏双方都准备好了
                //通知玩家1
                noticeGameReady(room, room.getUser1().getUserId(),
                        room.getUser2().getUserId());
                // 通知玩家2 就绪
                noticeGameReady(room, room.getUser2().getUserId(),
                        room.getUser1().getUserId());
                return;
            }

            //5.如果有第三个玩家尝试也想加⼊房间, 则给出⼀个提⽰, 房间已经满了.
            gameReadyResponse.setOk(false);
            gameReadyResponse.setReason("房间已满，连接游戏失败");
            session.sendMessage(new
                    TextMessage(objectMapper.writeValueAsString(gameReadyResponse)));
            System.out.println("房间已满，连接游戏失败");
        }
    }
    private void noticeGameReady(Room room,int thisUserId,int thatUserId) {
        try {
            GameReadyResponse resp = new GameReadyResponse();
            resp.setOk(true);
            resp.setMessage("gameReady");
            resp.setRoomId(room.getRoomId());
            resp.setThatUserId(thatUserId);
            resp.setThisUserId(thisUserId);
            resp.setWhiteUserId(room.getWhiteUserId());
            WebSocketSession session = onlineUserManager.getFromGameRoom(thisUserId);
            session.sendMessage((new
                    TextMessage(objectMapper.writeValueAsString(resp))));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


    @Override
    public void handleMessage(WebSocketSession session, WebSocketMessage<?> message) throws Exception {
        //1.⾸先需要检测⽤⼾的登录状态. 从 Session 中拿到当前⽤⼾信息.
        User user = (User)session.getAttributes().get("user");
        if(user == null){
            System.out.println("当前玩家尚未登录");
            return;
        }
        //2.根据用户玩家id获取房间对象
        Room room = roomManager.getRoomByUserId(user.getUserId());
        //3.通过room对象来处理这次具体请求
        room.putChess((String)message.getPayload());
    }

    @Override
    public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
        User user = (User)session.getAttributes().get("user");
        if(user == null) {
            System.out.println("[handleTransportError] 当前玩家尚未登录! ");
            return;
        }
        WebSocketSession exitsession = onlineUserManager.getFromGameRoom(user.getUserId());
        if(exitsession == session) {
            // 加上这个判定, 目的是为了避免在多开的情况下, 第二个用户退出连接动作, 导致第一个用户的会话被删除.
            System.out.println("多开判定时，防止两个账户都被删除");

            return;
        }
        System.out.println("连接出错! userId=" + user.getUserId());
        onlineUserManager.exitGameRoom(user.getUserId());
    }

    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
        User user = (User) session.getAttributes().get("user");
        if (user == null) {
            return;
        }
        WebSocketSession existSession =
                onlineUserManager.getFromGameRoom(user.getUserId());
        //避免多开的情况下，删除第二个账号影响第一个账号
        if (existSession != session) {
            System.out.println("当前的会话不是玩家游戏中的会话, 不做任何处理!");
            return;
        }
        System.out.println("⽤⼾退出! userId=" + user.getUserId());
        onlineUserManager.exitGameRoom(user.getUserId());

        noticeThatUserWin(user);
    }
    private void noticeThatUserWin(User user) throws IOException {
        //1.根据当前玩家，找到所在玩家的房间
        Room room = roomManager.getRoomByUserId(user.getUserId());
        if (room == null) {
            //这个情况意味着房间已经被释放，也就是没有对手了
            System.out.println("房间已经释放, ⽆需通知!");
            return;
        }
        //2.根据房间找到对手
        User thatUser = (user == room.getUser1() ? room.getUser2() :
                room.getUser1());
        //3.找到对手的在线状态
        WebSocketSession session =
                onlineUserManager.getFromGameRoom(thatUser.getUserId());
        if (session == null) {
            //意味着对手也掉线了
            System.out.println(thatUser.getUserId() + " 该玩家已经下线, ⽆需通知!");
            return;
        }
        //4.构建一个响应，来通知对手，你是获胜方
        GameResponse resp = new GameResponse();
        resp.setUserId(thatUser.getUserId());
        resp.setWinner(thatUser.getUserId());
        session.sendMessage(new
                TextMessage(objectMapper.writeValueAsString(resp)));
        //5.更新玩家的分数信息
        User winuser = thatUser;
        User loseUser = user;
        userMapper.userWin(winuser);
        userMapper.userLose(loseUser);
        //6.释放房间对象
        roomManager.remove(room.getRoomId(),room.getUser1().getUserId(),room.getUser2().getUserId());
        //不移除积累多了会内存泄漏
    }
}
